package parser

import (
	"bufio"
	"errors"
	"godis/interface/resp"
	"godis/lib/logger"
	"godis/resp/reply"
	"io"
	"runtime/debug"
	"strconv"
	"strings"
)

// 将用户发给godis的信息解析

type Payload struct {
	Data resp.Reply
	Err  error
}

type readState struct {
	readingMultiLine  bool
	expectedArgsCount int
	msgType           byte
	args              [][]byte
	bulkLen           int64
}

// 如果需要解析并并且已经解析的参数个数达到了要求，就可以调用finished
func (s *readState) finished() bool {
	return s.expectedArgsCount > 0 && len(s.args) == s.expectedArgsCount
}

// tcp => handler => ParseStream
/**
this is important
*/
func ParseStream(reader io.Reader) <-chan *Payload {
	ch := make(chan *Payload)
	go parse0(reader, ch)
	return ch
}

func parse0(reader io.Reader, ch chan<- *Payload) {
	defer func() {
		if err := recover(); err != nil {
			logger.Error(string(debug.Stack()))
		}
	}()
	bufReader := bufio.NewReader(reader)
	var state readState
	var err error
	var msg []byte
	for true {
		var ioErr bool
		msg, ioErr, err = readLine(bufReader, &state)
		if err != nil {
			if ioErr {
				ch <- &Payload{
					Err: err,
				}
				close(ch)
				return
			}
			ch <- &Payload{
				Err: err,
			}
			state = readState{}
			continue
		}
		// 判断是不是多行解析模式
		if !state.readingMultiLine {
			// 没有打开多行解析模式
			if msg[0] == '*' { // *3\r\n  字符串数组
				err := parseMultiBulkHeader(msg, &state) // modify this state
				if err != nil {
					ch <- &Payload{
						Err: errors.New("protocol error: " + string(msg)),
					}
					state = readState{}
					continue
				}
				if state.expectedArgsCount == 0 {
					ch <- &Payload{
						Data: &reply.EmptyMultiBulkReply{},
					}
					state = readState{} // readstate重置
					continue
				}
			} else if msg[0] == '$' { // 单个字符串  $4\r\nPING\r\n 也属于多行模式
				err := parseBulkHeader(msg, &state) // modify the state
				if err != nil {
					ch <- &Payload{
						Err: errors.New("protocol error: " + string(msg)),
					}
					state = readState{}
					continue
				}
				if state.bulkLen == -1 { // $-1\r\n 空指令 不再解析直接重置
					ch <- &Payload{
						Data: &reply.NullBulkReply{},
					}
					state = readState{}
					continue
				}
			} else { // : + -
				result, err := parseSingleLineReply(msg)
				ch <- &Payload{
					Data: result,
					Err:  err,
				}
				state = readState{}
				continue
			}
		} else { // 被设置成多行解析模式之后，进入else
			err := readBody(msg, &state)
			if err != nil {
				ch <- &Payload{
					Err: errors.New("protocol error: " + string(msg)),
				}
				state = readState{}
				continue
			}
			// 解析完当前参数，然后判断一下后面还没有参数需要解析
			if state.finished() {
				var result resp.Reply
				if state.msgType == '*' {
					// multibulk reply
					result = reply.MakeMultiBulkReply(state.args)
				} else if state.msgType == '$' { // 单行
					// singlebulk reply
					result = reply.MakeBulkReply(state.args[0])
				}
				ch <- &Payload{
					Data: result,
					Err:  err,
				}
				state = readState{}
			}
		}
	}
}

func readLine(bufReader *bufio.Reader, state *readState) ([]byte, bool, error) {

	var msg []byte
	var err error
	// 1. 没有$预设 ， 就认为读到了一行 直接\r\n切分即可
	// *3\r\nset
	if state.bulkLen == 0 { // 表示没有预设的个数 直接使用\r\n切分即可
		msg, err = bufReader.ReadBytes('\n')
		if err != nil {
			return nil, true, err // io错误
		}
		if len(msg) == 0 || msg[len(msg)-2] != '\r' {
			return nil, false, errors.New("protocol error: " + string(msg)) // 协议错误
		}
	} else { // 2. 有$，就不能用\r\n切分  eg. $3 那么bulkLen=3
		msg = make([]byte, state.bulkLen+2)   // 读上最后的\r\n
		_, err := io.ReadFull(bufReader, msg) // what's this?
		if err != nil {
			return nil, true, err
		}
		if len(msg) == 0 || msg[len(msg)-2] != '\r' || msg[len(msg)-1] != '\n' {
			return nil, false, errors.New("protocol error: " + string(msg))
		}
		state.bulkLen = 0 // 解析完就作废了
	}
	return msg, false, nil
}

// $4\r\nPING\r\n
func parseBulkHeader(msg []byte, state *readState) error {
	var err error
	state.bulkLen, err = strconv.ParseInt(string(msg[1:len(msg)-2]), 10, 64)
	if err != nil {
		return errors.New("protocol error: " + string(msg))
	}
	if state.bulkLen == -1 {
		return nil
	} else if state.bulkLen > 0 {
		state.msgType = msg[0] // $
		state.readingMultiLine = true
		state.expectedArgsCount = 1
		state.args = make([][]byte, 0, 1)
		return nil
	} else {
		return errors.New("protocol error: " + string(msg))
	}

}

func parseMultiBulkHeader(msg []byte, state *readState) error {
	var err error
	var expectedLine uint64                                                  // 代表多行数组的数组个数
	expectedLine, err = strconv.ParseUint(string(msg[1:len(msg)-2]), 10, 32) // *300\r\n   msg[1:len(msg)-2]可以确保保留300
	if err != nil {
		return errors.New("protocol error: " + string(msg))
	}
	if expectedLine == 0 {
		state.expectedArgsCount = 0
		return nil
	} else if expectedLine > 0 {
		state.msgType = msg[0] // * 表示正在读一个数组
		state.readingMultiLine = true
		state.expectedArgsCount = int(expectedLine)
		state.args = make([][]byte, 0, expectedLine)
		return nil
	} else {
		return errors.New("protocol error: " + string(msg))
	}
}

// +OK\r\n -err\r\n
func parseSingleLineReply(msg []byte) (resp.Reply, error) {
	str := strings.TrimSuffix(string(msg), "\r\n")
	var result resp.Reply
	switch msg[0] {
	case '+':
		result = reply.MakeStatusReply(str[1:])
	case '-':
		result = reply.MakeErrReply(str[1:])
	case ':':
		val, err := strconv.ParseInt(str[1:], 10, 64)
		if err != nil {
			return nil, errors.New("protocol error: " + string(msg))
		}
		result = reply.MakeIntReply(val)
	}
	return result, nil

}

// $3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n
// PING\r\n
func readBody(msg []byte, state *readState) error {
	line := msg[0 : len(msg)-2]
	var err error
	// eg. $3
	if line[0] == '$' { // 一行字符串
		state.bulkLen, err = strconv.ParseInt(string(line[1:]), 10, 64)
		if err != nil {
			return errors.New("protocol error: " + string(msg))
		}
		// $0\r\n
		if state.bulkLen <= 0 {
			state.args = append(state.args, []byte{})
			state.bulkLen = 0 // 如果是-1改回0
		}
	} else { //
		state.args = append(state.args, line)
	}
	return nil
}
